import re
import sys
import string

class FindFile:
	def __init__(self, file_name):
		self.file_name = file_name
		self.afile = open(file_name, 'r')
		self.all_lines = self.afile.readlines()
		self.afile.close()

	def find_key(self, key, case_sensitive=True):
		if case_sensitive:
			return [i for i, line in enumerate(self.all_lines) if key in line]
		else:
			return [i for i, line in enumerate(self.all_lines) if key.lower() in line.lower()]

	def find_BunderA(self, A, B, case_sensitive=True):
		re_list = []
		flags = 0 if case_sensitive else re.IGNORECASE
		pattern_A = re.compile(A, flags)
		pattern_B = re.compile(B, flags)

		for i, line in enumerate(self.all_lines):
			if pattern_A.search(line):
				for j in range(i, len(self.all_lines)):
					if pattern_B.search(self.all_lines[j]):
						re_list.append((i, j))
						break
		return re_list

	def clean_split_words(self, text):
		words = text.split() 
		clean_words = []
		for word in words:
			stripped = word.strip(string.punctuation) 
			if stripped: 
				clean_words.append(stripped) 
		return clean_words 
 
class FindPin:
	def __init__(self, afile):
		self.ff = FindFile(afile)
		self.all_lines = self.ff.all_lines

	def get_lef_pin(self):
		key = ["PIN", "DIRECTION"]
		re_list = []
		re_index = self.ff.find_BunderA(key[0], key[1])
		for i, turple in enumerate(re_index):
			line_1 = self.all_lines[turple[0]]
			r1 = self.ff.clean_split_words(line_1)[-1]
			line_2 = self.all_lines[turple[1]]
			r2 = self.ff.clean_split_words(line_2)[-1]
			re_list.append((r1, r2))		
		return sorted(re_list, key=lambda x:x[0])

	def get_lib_pin(self):
		key = ["^pin", "^pg_pin", "direction", "direction"]
		re_list = []
		re_index = self.ff.find_BunderA(key[0], key[2])
		for i, turple in enumerate(re_index):
			line_1 = self.all_lines[turple[0]]
			r1 = line_1[5:-5]
			line_2 = self.all_lines[turple[1]]
			r2 = line_2[13:-3]
			re_list.append((r1, r2))

		re_index = self.ff.find_BunderA(key[1], key[3])
		for i, turple in enumerate(re_index):
			line_1 = self.all_lines[turple[0]]
			r1 = line_1[8:-5]
			line_2 = self.all_lines[turple[1]]
			r2 = line_2[13:-2]
			re_list.append((r1, r2))

		return sorted(re_list, key=lambda x:x[0])

def edit_text(pin_list, start_xy, layer, ori, n):
	target_text = ""

	if ori == "H":
		x1 = start_xy[0]-0.3
		y1 = start_xy[1]-0.16
		x2 = start_xy[0]
		y2 = start_xy[1]
	elif ori == "V":
		x1 = start_xy[0]
		y1 = start_xy[1]-0.3
		x2 = start_xy[0]+0.16
		y2 = start_xy[1]
	
	for pin in pin_list :
		target_text += "  PIN "+pin[0]+"\n"
		target_text += "    DIRECTION "+pin[1].upper()+" ;\n"
		target_text += "    USE SIGNAL ;\n"
		target_text += "    PORT\n"
		target_text += "      LAYER "+layer+" ;\n"
		if ori == "H":
			y1 -= 0.16*n
			y2 -= 0.16*n
			target_text += "        RECT {} {} {} {} ;\n".format(str(round(x1, 3)), str(round(y1, 3)), str(round(x2, 3)), str(round(y2, 3)))
		if ori == "V":
			x1 += 0.16*n
			x2 += 0.16*n
			target_text += "        RECT {} {} {} {} ;\n".format(str(round(x1, 3)), str(round(y1, 3)), str(round(x2, 3)), str(round(y2, 3)))
		target_text += "    END\n"
		target_text += "  END "+pin[0]+"\n"
	return target_text	

def OutLef(pin_list, size, start_xy, block_name, layer_list, ori):
	target_text = ""
	with open("./"+block_name+".lef", 'w') as f:
		target_text = "VERSION 5.8 ;\nBUSBITCHARS \"[]\" ;\nDIVIDERCHAR \"/\" ;\nMACRO "+block_name+"\n  CLASS BLOCK ;\n  ORIGIN 0 0 ;\n  FOREIGN "+block_name+" 0 0 ;\n  SIZE "+str(size[0])+" BY "+str(size[1])+" ;\n"
		if len(layer_list) >=2 :
			len_layer_list = len(layer_list)
			
			if ori == "V" :
				for i in range(len_layer_list):
					pin_list_p = pin_list[i::len_layer_list]
					start_xy_p = (start_xy[0]+i*0.16, start_xy[1])
					target_text += edit_text(pin_list_p, start_xy_p, layer_list[i], "V", len_layer_list)
				
			elif ori == "H" :
				for i in range(len_layer_list):
					pin_list_p = pin_list[i::len_layer_list]
					start_xy_p = (start_xy[0], start_xy[1]-0.16*i)
					target_text += edit_text(pin_list_p, start_xy_p, layer_list[i], "H", len_layer_list)
			pass
		else:
			target_text += edit_text(pin_list, start_xy, layer_list[0], ori)	

		target_text += "END "+block_name
		f.write(target_text)
		

#lib = sys.argv[1]
#name = sys.argv[2]

def get_lib(lib, name, size, start_xy, layer_list, ori):
	if lib == "NDPHY":
		path_lib = "/designs/cascade/ip/ig/NDPHY_SPEEDPLUS_FC/rev2.3.3/lib/"+name+"_tt_0p75v_25c_typical.lib"
	elif lib == "DDRPHY":
		path_lib = "/designs/cascade/ip/ig/DDRPHY/rev2.8.0/lib/"+name+"_tt_0p75v_25c_typical.lib"
	elif lib == "OSC2M":
		path_lib = "/designs/cascade/ip/ig/OSC2M/rev1.1/lib/OSC2M_tt_0p75v_25c_typical.lib"
	elif lib == "CAB":
		path_lib = "/designs/cascade/ip/ig/CAB/rev1.4.1/lib/CAB_tt_0p75v_25c_typical.lib"
	elif lib == "ONFI_VSS":
		path_lib = "/designs/cascade/ip/ig/ONFI_VSS/rev1.0.0/lib/"+name+"_tt_0p75v_25c_typical.lib"

	fp1 = FindPin(path_lib)
	a = fp1.get_lib_pin()	
	OutLef(a, size, start_xy, name, layer_list, ori)


get_lib("NDPHY", "ND_CM_SUBPHY", (562.02, 801.12), (0.0, 801.12), ["M7", "M9"], "V")
get_lib("NDPHY", "ND_IO_SUBPHY", (481.992, 801.12), (0.0, 801.12), ["M7", "M9"], "V")
get_lib("NDPHY", "ND_ASYNC", (40.014, 801.12), (0.0, 801.12), ["M7", "M9"], "V")
get_lib("NDPHY", "ND_CAL", (40.014, 801.12), (0.0, 801.12), ["M7", "M9"], "V")
get_lib("NDPHY", "ND_SYNC", (40.014, 801.12), (0.0, 801.12), ["M7", "M9"], "V")
get_lib("NDPHY", "ND_VREF", (65.892, 801.12), (0.0, 801.12), ["M7", "M9"], "V")

get_lib("NDPHY", "ND_CM_SUBPHY_H", (801.078, 566.4), (801.078, 566.4), ["M6", "M8"], "H")
get_lib("NDPHY", "ND_IO_SUBPHY_H", (801.078, 485.76), (801.078, 485.76), ["M6", "M8"], "H")
get_lib("NDPHY", "ND_ASYNC_H", (801.078, 40.32), (801.078, 40.32), ["M6", "M8"], "H")
get_lib("NDPHY", "ND_CAL_H", (801.078, 40.32), (801.078, 40.32), ["M6", "M8"], "H")
get_lib("NDPHY", "ND_SYNC_H", (801.078, 40.32), (801.078, 40.32), ["M6", "M8"], "H")
get_lib("NDPHY", "ND_VREF_H", (801.078, 66.24), (801.078, 66.24), ["M6", "M8"], "H")


get_lib("DDRPHY", "DDR_ASYNC", (40.014, 700.8), (0.0, 700.8), ["M7", "M9"], "V")
get_lib("DDRPHY", "DDR_CAL", (40.014, 700.8), (0.0, 700.8), ["M7", "M9"], "V")
get_lib("DDRPHY", "DDR_IO_SUBPHY_13", (562.02, 700.8), (0.0, 700.8), ["M7", "M9"], "V")
get_lib("DDRPHY", "DDR_VGEN", (65.892, 700.8), (0.0, 700.8), ["M7", "M9"], "V")

get_lib("DDRPHY", "DDR_ASYNC_H", (701.1, 40.32), (701.1, 40.32), ["M6", "M8"], "H")
get_lib("DDRPHY", "DDR_CAL_H", (701.1, 40.32), (701.1, 40.32), ["M6", "M8"], "H")
get_lib("DDRPHY", "DDR_IO_SUBPHY_13_H", (701.1, 566.4), (701.1, 566.4), ["M6", "M8"], "H")
get_lib("DDRPHY", "DDR_VGEN_H", (701.1, 66.24), (701.1, 66.24), ["M6", "M8"], "H")

get_lib("ONFI_VSS", "MIONFI4VSS_V", (27.36, 801.12), (0.0, 801.12), ["M7", "M9"], "V")
get_lib("ONFI_VSS", "MIONFI4VSS_H", (801.078, 27.36), (801.078, 27.36), ["M6", "M8"], "H")